package com.gmss.crawler.utils;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;

import javax.net.ssl.*;
import java.io.File;
import java.io.IOException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @description: OkHttp请求
 **/
@Slf4j
public class OkHttp3Util {

    private OkHttp3Util() {
    }


    private static class TrustAllHostnameVerifier implements HostnameVerifier {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    }

    private static SSLSocketFactory createSSLSocketFactory() {
        SSLSocketFactory ssfFactory = null;
        try {
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(null, new TrustManager[]{new TrustAllCerts()}, new SecureRandom());
            ssfFactory = sc.getSocketFactory();
        } catch (Exception e) {
            log.error("构造SSL访问失败！", e);
        }
        return ssfFactory;
    }

    private static class TrustAllCerts implements X509TrustManager {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    }

    /**
     * @param url
     * @param params
     * @return
     * @throws IOException
     */
    public static Response doPost(String url, Map<String, String> params) throws IOException {
        return doPost(url, params, 0, 0, false);
    }

    /**
     * @param url
     * @param params
     * @param useProxy
     * @return
     * @throws IOException
     */
    public static Response doPost(String url, Map<String, String> params, boolean useProxy) throws IOException {
        return doPost(url, params, 0, 0, useProxy);
    }

    /**
     * @param url
     * @param params
     * @param timeOut
     * @param retry
     * @return
     * @throws IOException
     */
    public static Response doPost(String url, Map<String, String> params, int timeOut, int retry) throws IOException {
        return doPost(url, params, timeOut, timeOut, timeOut, retry, false);
    }

    /**
     * @param url
     * @param params
     * @param timeOut
     * @param retry
     * @param useProxy
     * @return
     * @throws IOException
     */
    public static Response doPost(String url, Map<String, String> params, int timeOut, int retry, boolean useProxy) throws IOException {
        return doPost(url, params, timeOut, timeOut, timeOut, retry, useProxy);
    }


    /**
     * 文件传输
     *
     * @param url      请求地址
     * @param params   请求参数
     * @param timeOut  超时时间
     * @param retry    重试次数
     * @param useProxy 代理设置，true:走代理，false:不走代理
     * @param fileMap  文件map，要求传输文件时用到
     *                 fileKey:字段名称（String）
     *                 fileName:文件名称（String）
     *                 fileBody:文件（File）
     * @return
     * @throws IOException
     */
    public static Response doPostFile(String url, Map<String, String> params, int timeOut, int retry, boolean useProxy, Map<String, Object> fileMap) throws IOException {
        return doPost(url, params, timeOut, timeOut, timeOut, retry, useProxy, fileMap, null);

    }

    /**
     * 设置hears头信息
     *
     * @param url      请求地址
     * @param params   请求参数
     * @param timeOut  超时时间
     * @param retry    重试次数
     * @param useProxy 代理设置，true:走代理，false:不走代理
     * @param hearsMap 设置hears
     * @return
     * @throws IOException
     */
    public static Response doPostHeads(String url, Map<String, String> params, int timeOut, int retry, boolean useProxy, Map<String, String> hearsMap) throws IOException {
        return doPost(url, params, timeOut, timeOut, timeOut, retry, useProxy, null, hearsMap);

    }


    /**
     * 设置hears头信息、传输文件
     *
     * @param url      请求地址
     * @param params   请求参数
     * @param timeOut  超时时间
     * @param retry    重试次数
     * @param useProxy 代理设置，true:走代理，false:不走代理
     * @param fileMap  文件map，要求传输文件时用到
     *                 fileKey:字段名称（String）
     *                 fileName:文件名称（String）
     *                 fileBody:文件（File）
     * @param hearsMap 设置hears
     * @return
     * @throws IOException
     */
    public static Response doPostFileWithHeads(String url, Map<String, String> params, int timeOut, int retry, boolean useProxy, Map<String, Object> fileMap, Map<String, String> hearsMap) throws IOException {
        return doPost(url, params, timeOut, timeOut, timeOut, retry, useProxy, fileMap, hearsMap);

    }

    /**
     * @param url            请求地址
     * @param params         请求参数
     * @param connectTimeOut 连接超时时间
     * @param readTimeOut    读超时时间
     * @param writeTimeOut   写超时时间
     * @param retry          重试次数
     * @param useProxy       代理设置，true:走代理，false:不走代理
     * @param fileMap        文件map，要求传输文件时用到
     *                       fileKey:字段名称（String）
     *                       fileName:文件名称（String）
     *                       fileBody:文件（File）
     * @param hearsMap       设置hears
     * @return
     * @throws IOException
     */
    public static Response doPost(String url, Map<String, String> params, int connectTimeOut, int readTimeOut, int writeTimeOut, int retry, boolean useProxy, Map<String, Object> fileMap, Map<String, String> hearsMap) throws IOException {
        OkHttpClient okHttpClient = new OkHttpClient();
        OkHttpClient.Builder okHttpClientBuilder = getOkHttpClientBuilder(okHttpClient, useProxy, url);
        if (connectTimeOut > 0) {
            okHttpClientBuilder.connectTimeout((long) connectTimeOut, TimeUnit.MILLISECONDS);
        }

        if (readTimeOut > 0) {
            okHttpClientBuilder.readTimeout((long) readTimeOut, TimeUnit.MILLISECONDS);
        }

        if (writeTimeOut > 0) {
            okHttpClientBuilder.writeTimeout((long) writeTimeOut, TimeUnit.MILLISECONDS);
        }

        okHttpClient = okHttpClientBuilder.build();

        MultipartBody.Builder builder = new MultipartBody.Builder();
        builder.setType(MultipartBody.FORM);

        if (params != null && !params.isEmpty()) {
            Iterator var10 = params.entrySet().iterator();

            while (var10.hasNext()) {
                Map.Entry entry = (Map.Entry) var10.next();
                String key = entry.getKey().toString();
                String value = entry.getValue().toString();
                builder.addFormDataPart(key, value);
            }
        }
        if (null != fileMap && !fileMap.isEmpty()) {
            File file = null;
            String fileKey = null;
            String fileName = null;
            if (!fileMap.containsKey("fileKey") || !fileMap.containsKey("fileName") || !fileMap.containsKey("fileBody")) {
                file = ((File) fileMap.get("fileBody"));
                if (null != file && !file.exists()) {
                    throw new IOException("文件fileMap，传输文件时用到,fileBody:文件（File）");
                }
                throw new IOException("文件fileMap，传输文件时用到,fileKey:字段名称（String）,fileName:文件名称（String）,fileBody:文件（File）");
            } else {
                fileKey = ((String) fileMap.get("fileKey"));
                fileName = ((String) fileMap.get("fileName"));
                file = ((File) fileMap.get("fileBody"));
            }

            builder.addFormDataPart(fileKey, fileName, RequestBody.create
                    (MediaType.parse("application/octet-stream"), file));
        }

        MultipartBody multipartBody = builder.build();
        Request.Builder requestBuilder = new Request.Builder().url(url).post(multipartBody);
        if (null != hearsMap && !hearsMap.isEmpty()) {
            Iterator var14 = hearsMap.entrySet().iterator();

            while (var14.hasNext()) {
                Map.Entry<String, String> e = (Map.Entry) var14.next();
                requestBuilder.addHeader(e.getKey(), e.getValue());
            }
        } else {
            requestBuilder.addHeader("Connection", "close");
        }

        Request request = requestBuilder.build();
        return retryProcess(okHttpClient, request, retry);
    }


    /**
     * @param url
     * @param params
     * @param connectTimeOut
     * @param readTimeOut
     * @param writeTimeOut
     * @param retry
     * @param useProxy
     * @return
     * @throws IOException
     */
    public static Response doPost(String url, Map<String, String> params, int connectTimeOut, int readTimeOut, int writeTimeOut, int retry, boolean useProxy) throws IOException {
        OkHttpClient okHttpClient = new OkHttpClient();
        OkHttpClient.Builder okHttpClientBuilder = getOkHttpClientBuilder(okHttpClient, useProxy, url);

        if (connectTimeOut > 0) {
            okHttpClientBuilder.connectTimeout(connectTimeOut, TimeUnit.MILLISECONDS);
        }
        if (readTimeOut > 0) {
            okHttpClientBuilder.readTimeout(readTimeOut, TimeUnit.MILLISECONDS);
        }
        if (writeTimeOut > 0) {
            okHttpClientBuilder.writeTimeout(writeTimeOut, TimeUnit.MILLISECONDS);
        }

        okHttpClient = okHttpClientBuilder.build();

        FormBody.Builder builder = new FormBody.Builder();
        if (params != null && !params.isEmpty()) {
            for (Map.Entry entry : params.entrySet()) {
                String key = entry.getKey().toString();
                String value = entry.getValue().toString();
                builder.add(key, value);
            }
        }
        RequestBody body = builder.build();
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .addHeader("Connection", "close")
                .build();

        return retryProcess(okHttpClient, request, retry);
    }


    /**
     * @param url
     * @param jsonStr
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(String url, String jsonStr) throws IOException {
        return doPostJSON(url, jsonStr, 0, 0, false);
    }

    /**
     * @param url
     * @param jsonStr
     * @param useProxy
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(String url, String jsonStr, boolean useProxy) throws IOException {
        return doPostJSON(url, jsonStr, 0, 0, useProxy);
    }

    /**
     * @param url
     * @param jsonStr
     * @param timeOut
     * @param retry
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(String url, String jsonStr, int timeOut, int retry) throws IOException {
        return doPostJSON(url, jsonStr, timeOut, timeOut, timeOut, retry, false);
    }

    /**
     * @param url
     * @param jsonStr
     * @param timeOut
     * @param retry
     * @param useProxy
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(String url, String jsonStr, int timeOut, int retry, boolean useProxy) throws IOException {
        return doPostJSON(url, jsonStr, timeOut, timeOut, timeOut, retry, useProxy);
    }

    /**
     * @param url
     * @param jsonStr
     * @param connectTimeOut
     * @param readTimeOut
     * @param writeTimeOut
     * @param retry
     * @param useProxy
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(String url, String jsonStr, int connectTimeOut, int readTimeOut, int writeTimeOut, int retry, boolean useProxy) throws IOException {
        MediaType JSON = MediaType.parse("application/json; charset=UTF-8");

        OkHttpClient okHttpClient = new OkHttpClient();
        OkHttpClient.Builder okHttpClientBuilder = getOkHttpClientBuilder(okHttpClient, useProxy, url);
        if (connectTimeOut > 0) {
            okHttpClientBuilder.connectTimeout(connectTimeOut, TimeUnit.MILLISECONDS);
        }
        if (readTimeOut > 0) {
            okHttpClientBuilder.readTimeout(readTimeOut, TimeUnit.MILLISECONDS);
        }
        if (writeTimeOut > 0) {
            okHttpClientBuilder.writeTimeout(writeTimeOut, TimeUnit.MILLISECONDS);
        }

        okHttpClient = okHttpClientBuilder.build();
//        if (jsonStr instanceof JSONObject) {
//            JSONObject jsonObj = JSONObject.parseObject(jsonStr);
//        } else if (jsonStr instanceof JSONArray) {
//            JSONArray jsonObj = JSONArray.parseArray(jsonStr);
//        } else {
//            throw new IOException("请求参数不是JSON格式！");
//        }

        RequestBody body = RequestBody.create(JSON, jsonStr);

        Request.Builder requestBuilder = (new okhttp3.Request.Builder())
                .url(url).post(body);

        requestBuilder.addHeader("Connection", "close");


        okhttp3.Request request = requestBuilder.build();

        return retryProcess(okHttpClient, request, retry);
    }

    /**
     * @param url
     * @param params
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(String url, Map<String, String> params) throws IOException {
        return doPostJSON(url, params, 0, 0, false);
    }

    /**
     * @param url
     * @param params
     * @param useProxy
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(String url, Map<String, String> params, boolean useProxy) throws IOException {
        return doPostJSON(url, params, 0, 0, useProxy);
    }

    /**
     * @param url
     * @param params
     * @param timeOut
     * @param retry
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(String url, Map<String, String> params, int timeOut, int retry) throws IOException {
        return doPostJSON(url, params, timeOut, timeOut, timeOut, retry, false);
    }

    /**
     * @param url
     * @param params
     * @param timeOut
     * @param retry
     * @param useProxy
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(String url, Map<String, String> params, int timeOut, int retry, boolean useProxy) throws IOException {
        return doPostJSON(url, params, timeOut, timeOut, timeOut, retry, useProxy);
    }

    /**
     * @param url
     * @param params
     * @param connectTimeOut
     * @param readTimeOut
     * @param writeTimeOut
     * @param retry
     * @param useProxy
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(String url, Map<String, String> params, int connectTimeOut, int readTimeOut, int writeTimeOut, int retry, boolean useProxy) throws IOException {
        MediaType JSON = MediaType.parse("application/json; charset=utf-8");

        OkHttpClient okHttpClient = new OkHttpClient();
        OkHttpClient.Builder okHttpClientBuilder = getOkHttpClientBuilder(okHttpClient, useProxy, url);
        if (connectTimeOut > 0) {
            okHttpClientBuilder.connectTimeout(connectTimeOut, TimeUnit.MILLISECONDS);
        }
        if (readTimeOut > 0) {
            okHttpClientBuilder.readTimeout(readTimeOut, TimeUnit.MILLISECONDS);
        }
        if (writeTimeOut > 0) {
            okHttpClientBuilder.writeTimeout(writeTimeOut, TimeUnit.MILLISECONDS);
        }

        okHttpClient = okHttpClientBuilder.build();
        JSONObject jsonObj = JSONObject.parseObject(JSONObject.toJSONString(params));

        RequestBody body = RequestBody.create(JSON, jsonObj.toJSONString());

        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .addHeader("Connection", "close")
                .build();

        return retryProcess(okHttpClient, request, retry);
    }

    /**
     * @param request
     * @param isHttps
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(Request request, boolean isHttps) throws IOException {
        return doPostJSON(request, 0, 0, isHttps, false);
    }

    /**
     * @param request
     * @param isHttps
     * @param useProxy
     * @param proxyConfig
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(Request request, boolean isHttps, boolean useProxy, JSONObject proxyConfig) throws IOException {
        return doPostJSON(request, 0, 0, isHttps, useProxy);
    }

    /**
     * @param request
     * @param timeOut
     * @param retry
     * @param isHttps
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(Request request, int timeOut, int retry, boolean isHttps) throws IOException {
        return doPostJSON(request, timeOut, timeOut, timeOut, retry, isHttps, false);
    }

    /**
     * @param request
     * @param timeOut
     * @param retry
     * @param isHttps
     * @param useProxy
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(Request request, int timeOut, int retry, boolean isHttps, boolean useProxy) throws IOException {
        return doPostJSON(request, timeOut, timeOut, timeOut, retry, isHttps, useProxy);
    }


    /**
     * @param request
     * @param connectTimeOut
     * @param readTimeOut
     * @param writeTimeOut
     * @param retry
     * @param isHttps
     * @param useProxy
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(Request request, int connectTimeOut, int readTimeOut, int writeTimeOut, int retry, boolean isHttps, boolean useProxy) throws IOException {
        OkHttpClient okHttpClient = new OkHttpClient();
        OkHttpClient.Builder okHttpClientBuilder = getOkHttpClientBuilder(okHttpClient, useProxy, null);
        if (isHttps) {
            okHttpClientBuilder.sslSocketFactory(createSSLSocketFactory(), new TrustAllCerts())
                    .hostnameVerifier(new TrustAllHostnameVerifier());
        }
        if (connectTimeOut > 0) {
            okHttpClientBuilder.connectTimeout(connectTimeOut, TimeUnit.MILLISECONDS);
        }
        if (readTimeOut > 0) {
            okHttpClientBuilder.readTimeout(readTimeOut, TimeUnit.MILLISECONDS);
        }
        if (writeTimeOut > 0) {
            okHttpClientBuilder.writeTimeout(writeTimeOut, TimeUnit.MILLISECONDS);
        }

        return retryProcess(okHttpClientBuilder.build(), request, retry);
    }

    /**
     * @param url
     * @param jsonStr
     * @param timeOut
     * @param retry
     * @param useProxy
     * @param hearsMap
     * @return
     * @throws IOException
     */
    public static Response doPostJson(String url, String jsonStr, int timeOut, int retry, boolean useProxy, Map<String, String> hearsMap) throws IOException {
        return doPostJSON(url, jsonStr, timeOut, timeOut, timeOut, retry, useProxy, hearsMap);
    }

    /**
     * @param url
     * @param jsonStr
     * @param connectTimeOut
     * @param readTimeOut
     * @param writeTimeOut
     * @param retry
     * @param useProxy
     * @param hearsMap
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(String url, String jsonStr, int connectTimeOut, int readTimeOut, int writeTimeOut, int retry, boolean useProxy, Map<String, String> hearsMap) throws IOException {
        MediaType JSON = MediaType.parse("application/json, text/plain; charset=UTF-8");

        OkHttpClient okHttpClient = new OkHttpClient();
        OkHttpClient.Builder okHttpClientBuilder = getOkHttpClientBuilder(okHttpClient, useProxy, url);
        if (connectTimeOut > 0) {
            okHttpClientBuilder.connectTimeout(connectTimeOut, TimeUnit.MILLISECONDS);
        }
        if (readTimeOut > 0) {
            okHttpClientBuilder.readTimeout(readTimeOut, TimeUnit.MILLISECONDS);
        }
        if (writeTimeOut > 0) {
            okHttpClientBuilder.writeTimeout(writeTimeOut, TimeUnit.MILLISECONDS);
        }

        okHttpClient = okHttpClientBuilder.build();
        JSONObject jsonObj = JSONObject.parseObject(jsonStr);

        RequestBody body = RequestBody.create(JSON, jsonObj.toJSONString());

        Request.Builder requestBuilder = (new okhttp3.Request.Builder())
                .url(url).post(body);

        if (null != hearsMap && hearsMap.size() > 0) {
            for (Map.Entry<String, String> e : hearsMap.entrySet()) {
                requestBuilder.addHeader(e.getKey(), e.getValue());
            }
        } else {
            requestBuilder.addHeader("Connection", "close");
        }

        okhttp3.Request request = requestBuilder.build();

        return retryProcess(okHttpClient, request, retry);
    }

    /**
     * @param url
     * @param params
     * @return
     * @throws IOException
     */
    public static Response doGet(String url, Map<String, String> params) throws IOException {
        return doGet(url, params, 0, 0, false);
    }

    /**
     * @param url
     * @param params
     * @param useProxy
     * @return
     * @throws IOException
     */
    public static Response doGet(String url, Map<String, String> params, boolean useProxy) throws IOException {
        return doGet(url, params, 0, 0, useProxy);
    }

    /**
     * @param url
     * @param params
     * @param timeOut
     * @param retry
     * @return
     * @throws IOException
     */
    public static Response doGet(String url, Map<String, String> params, int timeOut, int retry) throws IOException {
        return doGet(url, params, timeOut, timeOut, timeOut, retry, false);
    }

    /**
     * @param url
     * @param params
     * @param timeOut
     * @param retry
     * @param useProxy
     * @return
     * @throws IOException
     */
    public static Response doGet(String url, Map<String, String> params, int timeOut, int retry, boolean useProxy) throws IOException {
        return doGet(url, params, timeOut, timeOut, timeOut, retry, useProxy);
    }

    /**
     * @param url
     * @param params
     * @param connectTimeOut
     * @param readTimeOut
     * @param writeTimeOut
     * @param retry
     * @param useProxy
     * @return
     * @throws IOException
     */
    public static Response doGet(String url, Map<String, String> params, int connectTimeOut, int readTimeOut, int writeTimeOut, int retry, boolean useProxy) throws IOException {
        OkHttpClient okHttpClient = new OkHttpClient();
        OkHttpClient.Builder okHttpClientBuilder = getOkHttpClientBuilder(okHttpClient, useProxy, url);
        if (connectTimeOut > 0) {
            okHttpClientBuilder.connectTimeout(connectTimeOut, TimeUnit.MILLISECONDS);
        }
        if (readTimeOut > 0) {
            okHttpClientBuilder.readTimeout(readTimeOut, TimeUnit.MILLISECONDS);
        }
        if (writeTimeOut > 0) {
            okHttpClientBuilder.writeTimeout(writeTimeOut, TimeUnit.MILLISECONDS);
        }

        okHttpClient = okHttpClientBuilder.build();

        //封装get请求的url
        if (params != null && !params.isEmpty()) {
            for (Map.Entry entry : params.entrySet()) {
                String key = entry.getKey().toString();
                String value = entry.getValue().toString();
                if (url.indexOf("?") > 0) {
                    url += "&" + key + "=" + value;
                } else {
                    url += "?" + key + "=" + value;
                }
            }
        }

        Request request = new Request.Builder().url(url).
                addHeader("Connection", "close").build();

        return retryProcess(okHttpClient, request, retry);
    }


    /**
     * @param okHttpClient
     * @param request
     * @param retry
     * @return
     * @throws IOException
     */
    private static Response retryProcess(OkHttpClient okHttpClient, Request request, int retry) throws IOException {
        Response response = null;
        IOException ioE = null;
        if (retry > 0) {
            while (retry > 0) {
                try {
                    Call call = okHttpClient.newCall(request);
                    response = call.execute();
                } catch (IOException e) {
                    response = null;
                    try {
                        Thread.sleep(CommonUtils.randomRange(100, 500));
                    } catch (InterruptedException e1) {
                    }
                    log.error("链接超时或失败 url:" + request.url());
                    ioE = e;
                }
                retry--;
                if (null != response) {
                    break;
                }
            }
            if (response != null) {
                return response;
            } else {
                throw ioE;
            }
        } else {
            Call call = okHttpClient.newCall(request);
            return call.execute();
        }
    }

    /**
     * @param okHttpClient
     * @param url
     * @return
     */
    private static OkHttpClient.Builder getOkHttpClientBuilder(OkHttpClient okHttpClient, boolean useProxy, String url) {
        OkHttpClient.Builder okHttpClientBuilder = okHttpClient.newBuilder();
        if (url != null && url.startsWith("https")) {
            okHttpClientBuilder.sslSocketFactory(createSSLSocketFactory(), new TrustAllCerts())
                    .hostnameVerifier(new TrustAllHostnameVerifier());
            return okHttpClientBuilder;
        }
        return okHttpClientBuilder;
    }
}

